﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Nes.Core.PPUS;

namespace Nes.Core.Buss
{
    public class CPUBus : IBus
    {
        private readonly ICartridge cartridge;
        private readonly Ram ram = new (0x0800);

        public APUS.APU APU { get; internal set; }
        public PPU PPU { get; internal set; }
        public DMA DMA { get; internal set; }
        public StdController Ctrl1 { get; set; }
        public StdController Ctrl2 { get; set; }


        public CPUBus(ICartridge cartridge)
        {
            this.cartridge = cartridge;
        }


        public Byte ReadByte(Int32 address)
        {
            if (address < 0x2000)
            {
                // RAM
                return this.ram.Read(address & 0x07FF);
            }
            else if (address < 0x4000)
            {
                // PPU
                return PPU.CPURead(address & 0x2007);
            }
            else if (address == 0x4014)
            {
                return 0;
            }
            else if (address == 0x4016) 
            {
                // 手柄1
                return Ctrl1.Read();
            }
            else if (address == 0x4017) 
            {
                return Ctrl2.Read();
            }
            else if (address < 0x4018)
            {
                // [0x4000,0x4013] [0x4015] 
                // APU
                return APU.Read(address);
            }
            else if (address < 0x4020)
            {
                // 0x4018, 0x4019
                return 0;
            }
            else
            {
                // ROM
                return this.cartridge.Mapper.Read(address);
            }
        }

        public UInt16 ReadWord(Int32 address)
        {
            return (UInt16)(ReadByte(address + 1) << 8 | ReadByte(address));
        }

        public void WriteByte(Int32 address, Byte data)
        {
            if (address < 0x2000)
            {
                // RAM
                this.ram.Write(address & 0x07FF, data);
            }
            else if (address < 0x4000)
            {
                this.PPU.CPUWrite(address & 0x2007, data);
            }
            else if (address == 0x4014)
            {
                // DMA
                this.DMA.Copy(data << 8);
            }
            else if (address == 0x4016)
            {
                // 两个 手柄写入
                Ctrl1.Wirte(data);
                Ctrl2.Wirte(data);
            }
            else if (address < 0x4017)
            {
                // APU
                APU.Write(address, data);
            }
            else if (address < 0x4020)
            {
                // DO NOTHING
            }
            else
            {
                // Cartridge
                this.cartridge.Mapper.Write(address, data);
            }
        }

        public void WriteWord(Int32 address, UInt16 data)
        {
            var datas = BitConverter.GetBytes(data);
            WriteByte(address, datas[0]);
            WriteByte(address + 1, datas[1]);
        }
    }
}
